﻿///////////////////////////////////////////////////////
//
// XDesigner.ORM 
//
// autohr : yuans
//
// date: 2011-2-23
//
// email  : yyf9989@hotmail.com
// 
//
///////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using XDesigner.ORM;
using System.Data;
using System.Collections;
using XDesigner.Common;

namespace XDesigner.ORM.DOM
{
    /// <summary>
    /// ORMTypeHelper base on reflect technology
    /// </summary>
    internal class ReflectionORMTypeMappingHelper : ORMTypeMappingHelper
    {
        public ReflectionORMTypeMappingHelper(ORMTypeInfo type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }
            this.ORMType = type;
        }


        public override bool FillInstance(
            ORMContext context ,
            IDataReader reader,
            object instance,
            List<RelationPropertyValue> relationValues)
        {
            if (reader == null)
            {
                throw new ArgumentNullException("reader");
            }
            if (instance == null)
            {
                throw new ArgumentNullException("instance");
            }

            int fieldCount = reader.FieldCount;
            //if (instance.GetType().Name == "OrderDetailClass")
            //{
            //    System.Diagnostics.Debugger.Break();
            //}
            for (int iCount = 0; iCount < fieldCount; iCount++)
            {
                string name = reader.GetName(iCount);
                foreach (ORMPropertyInfo p in this.ORMType.Properties)
                {
                    if (p.MatchDBFieldName(name) == false)
                    {
                        continue;
                    }
                    if (p.Ignore || p.DelayLoad)
                    {
                        continue;
                    }
                    object Value = reader.GetValue(iCount);
                    if (p.HasRelation)
                    {
                        if (p.Relation.RuntimeStyle == ORMRelationStyle.OneToOne)
                        {
                            // when OneToOne relation , try to get instance from context buffer.
                            object subInstance = context.Buffer.GetInstance(
                                Value, p.PropertyType );
                            if (subInstance == null)
                            {
                                subInstance = context.Engine.Buffer.GetInstance(
                                    Value, p.PropertyType);
                            }
                            if (subInstance != null)
                            {
                                this.SetPropertyValue(instance, p.Name, subInstance);
                                continue;
                            }
                        }
                        if (relationValues != null)
                        {
                            relationValues.Add(new RelationPropertyValue(instance, p , Value));
                        }
                    }
                    else
                    {
                        object newValue = null;
                        if (XDesigner.Common.ValueTypeHelper.TryConvertTo(
                            Value,
                            p.PropertyInfo.PropertyType,
                            ref newValue))
                        {
                            this.SetPropertyValue(instance, p.Name, newValue);
                            //p.SetValue(instance, newValue);
                        }
                    }
                }//foreach
            }//for
            return true;
        }

         
        public override object GetPropertyValue(object instance, string name)
        {
            ORMPropertyInfo p = this.ORMType.Properties[name];
            return p.PropertyInfo.GetValue(instance, null);
        }

        public override void SetPropertyValue(object instance, string name, object Value)
        {
            ORMPropertyInfo p = this.ORMType.Properties[name];
            p.PropertyInfo.SetValue(instance, Value, null);
        }
         

        public override string SelectCommandText
        {
            get
            {
                return this.ORMType.GetSelectCommandText();
            }
        }

        internal string GetDBFieldList()
        {
            StringBuilder str = new StringBuilder();
            foreach (ORMPropertyInfo p in this.ORMType.Properties)
            {
                if (p.Ignore == false && p.DelayLoad == false)
                {
                    if (p.HasRelation)
                    {
                        if (p.Relation.RuntimeStyle == ORMRelationStyle.OneToMany)
                        {
                            continue;
                        }
                        if (Util.HasContent(p.DBFieldName) == false)
                        {
                            continue;
                        }
                    }
                    if (str.Length > 0)
                    {
                        str.Append(" , ");
                    }
                    str.Append(p.DBFieldName);
                }//if
            }
            return str.ToString();
        }

        public override object CreateInstance()
        {
            return System.Activator.CreateInstance(this.ORMType.InstanceType);
        }

        public override string GetPropertyCommandText(string name)
        {
            ORMPropertyInfo property = this.ORMType.Properties[name];
            if (Util.HasContent(property.CommandText))
            {
                return property.CommandText;
            }
            else
            {
                if (property.HasRelation)
                {
                    if (property.OwnerType == null)
                    {
                        throw new InvalidOperationException("Owner is null");
                    }
                    Type elementType = property.PropertyType;
                    ORMRelationStyle rs = property.Relation.RuntimeStyle;
                    if (rs == ORMRelationStyle.OneToMany
                        || rs == ORMRelationStyle.OneToManyWithKeyList
                        || rs == ORMRelationStyle.OnetoManyExt )
                    {
                        elementType = CollectionTypeHelper.GetElementType(property.PropertyType);
                    }
                    ORMTypeInfo childType = property.OwnerType.Project.Types[elementType];
                    if (childType == null)
                    {
                        throw new InvalidOperationException("childType is null");
                    }

                    ORMPropertyInfo parentProperty = property.Relation.ParentProperty;

                    ORMPropertyInfo childProperty = property.Relation.ChildProperty;
                    if (childProperty == null)
                    {
                        throw new InvalidOperationException("child property is null");
                    }

                    string strSQL = null;
                    if (rs == ORMRelationStyle.OneToManyWithKeyList)
                    {
                        strSQL = childType.GetSelectCommandText()
                            + " Where " + childProperty.DBFieldName
                            + " in ( [%@" + parentProperty.Name + "%] )";
                    }
                    else
                    {
                        strSQL = childType.GetSelectCommandText()
                            + " Where " + childProperty.DBFieldName
                            + " = [%@" + parentProperty.Name + "%]";
                        foreach (ORMPropertyInfo p in childType.Properties)
                        {
                            if (p.ExtCondition == ORMBooleanValue.True)
                            {
                                object v = System.Activator.CreateInstance(childType.InstanceType);
                                object pv = p.PropertyInfo.GetValue(v, null);
                                if (p.PropertyType.Equals(typeof(string)))
                                {
                                    strSQL = strSQL + " And " + p.RuntimeDBFieldName + " = '" + pv + "'";
                                }
                                else
                                {
                                    strSQL = strSQL + " And " + p.RuntimeDBFieldName + " = " + pv + "";
                                }
                            }
                        }
                    }
                    return strSQL;
                }
            }
            return null;
        }

        public override object GetDBValue(IDataReader reader, string propertyName)
        {
            if (reader == null)
            {
                throw new ArgumentNullException("reader");
            }
            ORMPropertyInfo p = this.ORMType.Properties[propertyName];
            if (p == null)
            {
                throw new NotSupportedException(propertyName);
            }
            int fieldCount = reader.FieldCount;
            for (int iCount = 0; iCount < fieldCount; iCount++)
            {
                string name = reader.GetName(iCount);
                if (string.Compare(name, p.DBFieldName, true) == 0)
                {
                    return reader.GetValue(iCount);
                }
                if (Util.HasContent(p.ComparabilityDBFieldName)
                    && string.Compare(name, p.ComparabilityDBFieldName, true) == 0)
                {
                    return reader.GetValue(iCount);
                }
                if (Util.HasContent(p.ComparabilityDBFieldName2)
                    && string.Compare(name, p.ComparabilityDBFieldName2, true) == 0)
                {
                    return reader.GetValue(iCount);
                }
                if (string.Compare(name, p.Name, true) == 0)
                {
                    return reader.GetValue(iCount);
                }
            }
            return DBNull.Value;
        }

        public override bool PrepareDeleteCommand( ORMContext context , object instance )
        {
            StringBuilder mySQL = new StringBuilder();
            ORMPropertyInfo keyProperty = this.ORMType.KeyProperty ;
            context.Command.Parameters.Clear();
            mySQL.Append("Delete From " + this.ORMType.TableName 
                + " Where " + keyProperty.RuntimeDBFieldName + " = ");
            object keyValue = null;
            if (context.ForGenerateCode)
            {
                keyValue = keyProperty;
            }
            else
            {
                keyValue = this.GetPropertyValue(instance, keyProperty.Name);
            }
            ORMDBCommand.AppendParameterSQL(
                mySQL , 
                keyProperty.RuntimeDBFieldName ,
                keyValue , 
                context.ParameterStyle ,
                context.Command );
            context.Command.CommandText = mySQL.ToString();
            return true;
        }

        public override object GetKeyValue(object instance)
        {
            ORMPropertyInfo keyProperty = this.ORMType.KeyProperty;
            if (keyProperty != null)
            {
                return GetPropertyValue(instance, keyProperty.Name);
            }
            else
            {
                throw new NotSupportedException("GetKeyValue method");
            }
        }

        public override bool PrepareInsertCommand(ORMContext context, object instance)
        {
            context.Command.Parameters.Clear();
            StringBuilder nameList = new StringBuilder();
            StringBuilder valueList = new StringBuilder();
            foreach (ORMPropertyInfo property in this.ORMType.Properties)
            {
                if (property.Ignore || property.DBReadonly )
                {
                    continue;
                }
                if (property.Relation != null)
                {
                    if (property.Relation.RuntimeStyle == ORMRelationStyle.OneToMany
                        || property.Relation.RuntimeStyle == ORMRelationStyle.OnetoManyExt)
                    {
                        continue;
                    }
                }

                string fieldName = property.RuntimeDBFieldName;
                if (Util.HasContent(fieldName) == false)
                {
                    continue;
                }
                if (nameList.Length > 0)
                {
                    nameList.Append(" , ");
                    valueList.Append(" , ");
                }
                nameList.Append(fieldName);
                object fieldValue = null;
                if (context.ForGenerateCode)
                {
                    fieldValue = property;
                }
                else
                {
                    fieldValue = this.GetDBPropertyValue(context, instance, property.Name);
                }
                ORMDBCommand.AppendParameterSQL(
                    valueList, 
                    fieldName,
                    fieldValue,
                    context.ParameterStyle,
                    context.Command);
            }//foreach
            if (nameList.Length > 0)
            {
                string strSQL = "Insert Into " + this.ORMType.TableName 
                    + " ( " + nameList.ToString() 
                    + " )\r\n Values ( " + valueList.ToString() + " )";
                context.Command.CommandText = strSQL;

                return true;
            }
            else
            {
                return false;
            }
        }

        public override bool PrepareUpdateCommand(ORMContext context, object instance)
        {
            context.Command.Parameters.Clear();

            ORMPropertyInfo keyProperty = this.ORMType.KeyProperty;
            if (keyProperty == null)
            {
                throw new InvalidOperationException(
                    this.ORMType.InstanceTypeName + "Not exist key field");
            }
            StringBuilder mySQL = new StringBuilder();
            foreach (ORMPropertyInfo property in this.ORMType.Properties)
            {
                if (property.Ignore || property.DBReadonly || property.KeyField)
                {
                    continue;
                }
                if (property.Relation != null)
                {
                    if (property.Relation.RuntimeStyle == ORMRelationStyle.OneToMany
                        || property.Relation.RuntimeStyle == ORMRelationStyle.OnetoManyExt)
                    {
                        continue;
                    }
                }
                string fieldName = property.RuntimeDBFieldName;
                if (Util.HasContent(fieldName) == false)
                {
                    continue;
                }
                if (mySQL.Length > 0)
                {
                    mySQL.Append(" , ");
                }
                mySQL.Append("\r\n   " + fieldName + " = ");
                object fieldValue = null ;
                if (context.ForGenerateCode)
                {
                    fieldValue = property;
                }
                else
                {
                    fieldValue = GetDBPropertyValue(context, instance, property.Name);
                }
                ORMDBCommand.AppendParameterSQL(
                    mySQL,
                    fieldName,
                    fieldValue,
                    context.ParameterStyle,
                    context.Command);
            }//foreach
            object keyValue = null;
            if (context.ForGenerateCode)
            {
                keyValue = keyProperty;
            }
            else
            {
                keyValue = GetDBPropertyValue(context, instance, keyProperty.Name);
            }
            mySQL.Insert(0, "Update " + this.ORMType.TableName + " Set ");
            mySQL.Append("\r\nWhere " + keyProperty.RuntimeDBFieldName + " = ");
            ORMDBCommand.AppendParameterSQL(
                mySQL,
                keyProperty.RuntimeDBFieldName,
                keyValue,
                context.ParameterStyle,
                context.Command);
            context.Command.CommandText = mySQL.ToString();
            return true;

        }

        public override void GenerateNewValue( ORMContext context , object instance)
        {
            //ORMPropertyInfo keyProperty = this.ORMType.KeyProperty;
            //if (keyProperty == null || keyProperty.IDGenerate == null )
            //{
            //    throw new NotSupportedException(
            //        this.ORMType.InstanceTypeName + " GenerateID");
            //}
            //switch (keyProperty.IDGenerate.Style)
            //{
            //    case ORMIDGenerateStyle.Increase:
            //        {
            //            return context.Engine.GenerateIncreaseID(
            //                context,
            //                this.ORMType.TableName,
            //                keyProperty.Name);
            //        }
            //    case ORMIDGenerateStyle.CurrentTime :
            //        DateTime dtm = DateTime.Now;
            //        if (Util.HasContent(keyProperty.IDGenerate.Format))
            //        {
            //            return dtm.ToString(keyProperty.IDGenerate.Format);
            //        }
            //        else
            //        {
            //            return dtm.ToString();
            //        }
            //    case ORMIDGenerateStyle.Custom :
            //        break;
            //    case ORMIDGenerateStyle.GUID :
            //        Guid guid = Guid.NewGuid();
            //        if (Util.HasContent(keyProperty.IDGenerate.Format))
            //        {
            //            return guid.ToString(keyProperty.IDGenerate.Format);
            //        }
            //        else
            //        {
            //            return guid.ToString();
            //        }
            //}
            //return null;
        }
    }//internal class ReflectionORMTypeHelper : ORMTypeHelper
}
